package puyu.manage.service.minio;

import io.minio.*;
import io.minio.http.Method;
import org.apache.commons.compress.utils.IOUtils;
import org.apache.commons.io.FilenameUtils;
import org.aspectj.util.FileUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import puyu.manage.service.constant.AjaxResult;
import puyu.manage.service.utils.IdUtils;

import javax.annotation.Resource;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

@Component
public class MinioUtil {


	@Resource
	private MinioClient minioClient;

	@Value("${minio.bucketName}")
	private String bucketName;

	/**
	 * 判断存储桶是否存在
	 *
	 * @return 桶是否存在
	 */
	public boolean bucketExists() {
		try {
			return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
		} catch (Exception e) {
			throw new RuntimeException("桶不存在", e);
		}
	}

	/**
	 * 创建桶
	 */
	public void makeBucket() {
		try {
			minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
		} catch (Exception e) {
			throw new RuntimeException("创建桶失败", e);
		}
	}

	/**
	 * 上传单个对象
	 *
	 * @param file 文件
	 * @return 上传成功
	 */
	public String putObject(MultipartFile file) {
		if (file == null || file.isEmpty())
			throw new IllegalArgumentException("文件不能为空");

		try (InputStream inputStream = file.getInputStream()){
			//检查存储桶是否存在
			if (!bucketExists())
				makeBucket();

			// 获取文件名
			String fileName = file.getOriginalFilename();

			assert fileName != null;
			// 生成随机的对象名称
			String objectName = IdUtils.simpleUUID() + "." + FilenameUtils.getExtension(fileName);

			// 添加自定义的元数据
			Map<String, String> meta = new HashMap<>();
			meta.put("file-name", fileName);

			// 添加对象
			minioClient.putObject(PutObjectArgs.builder()
					.bucket(bucketName)
					.object(objectName)
					.userMetadata(meta)
					.stream(inputStream, file.getSize(), -1)
					.contentType(file.getContentType())
					.build());
			return objectName;
		} catch (Exception e) {
			throw new RuntimeException("上传失败", e);
		}
	}

	/**
	 * 上传多个对象
	 *
	 * @param files 文件数组
	 * @return 上传成功
	 */
	public AjaxResult putObject(MultipartFile[] files) {
		for (MultipartFile file : files) {
			putObject(file);
		}
		return AjaxResult.success("上传成功");
	}

	/**
	 * 删除一个对象
	 *
	 * @param objectName 对象名称
	 * @return 是否删除成功
	 */
	public boolean removeObject(String objectName) {
		if (bucketExists()) {
			try {
				minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());
				return true;
			} catch (Exception e) {
				throw new RuntimeException("删除失败", e);
			}
		}
		return false;
	}


	/**
	 * 删除多个对象,返回删除失败的对象列表
	 *
	 * @param objectNames 对象List
	 * @return 删除失败的对象List
	 */
	public List<String> removeObjects(List<String> objectNames) {
		List<String> deleteErrorNames = new ArrayList<>();
		if (bucketExists()) {
			for (String objectName : objectNames) {
				if (!removeObject(objectName)) {
					deleteErrorNames.add(objectName);
				}
			}
		}
		return deleteErrorNames;
	}

	/**
	 * 获取对象的元数据
	 *
	 * @param objectName 对象名称
	 * @return 元数据
	 */
	public AjaxResult statObject(String objectName) {
		try {
			if (bucketExists()) {
				StatObjectResponse response = minioClient.statObject(StatObjectArgs.builder()
						.bucket(bucketName)
						.object(objectName)
						.build());
				// 输出对象的元数据信息
				Map<String, String> metadata = response.userMetadata();
				// 返回
				return AjaxResult.success(metadata);
			}
		} catch (Exception e) {
			throw new RuntimeException("获取元数据失败", e);
		}
		return null;
	}

	/**
	 * 获取文件的临时访问路径
	 *
	 * @param objectName 对象名称
	 * @return 临时访问url
	 */
	public String getObjectUrl(String objectName) {
		String url = "";
		try {
			if (bucketExists()) {
				// 设置预签名URL的有效期（以秒为单位）
				int expiresInSeconds = 3600; // 一小时
				url = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
						.method(Method.GET) //只能get请求访问
						.bucket(bucketName)
						.object(objectName)
						.expiry(expiresInSeconds) // 设置有效时间
						.build());
			}
		} catch (Exception e) {
			throw new RuntimeException("获取失败", e);
		}
		return url;
	}


	/**
	 * 获取（下载）单个文件
	 *
	 * @param fileName 文件名
	 * @return 输出流
	 */
	public ResponseEntity<ByteArrayResource> getObject(String fileName){
		// 获取输入流
		try (InputStream inputStream = minioClient.getObject(GetObjectArgs.builder()
				.bucket(bucketName)
				.object(fileName).build())) {

			byte[] byteArray = IOUtils.toByteArray(inputStream);
			ByteArrayResource resource = new ByteArrayResource(byteArray);

			//设置请求头
			HttpHeaders headers = new HttpHeaders();
			headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + fileName);

			//开始下载
			return ResponseEntity.ok()
					.headers(headers)
					.contentLength(byteArray.length)
					.contentType(MediaType.APPLICATION_OCTET_STREAM)
					.body(resource);
		}catch (Exception e) {
			throw new RuntimeException("下载失败", e);
		}
	}


	/**
	 * 获取（下载）多个文件，写进zip压缩包返回
	 *
	 * @param objectNames 资源名数组
	 * @return 输出流
	 */
	public ResponseEntity<byte[]> getObjectList(String[] objectNames) {
		// 创建一个输出流，将文件写入压缩包
		try (ByteArrayOutputStream aos = new ByteArrayOutputStream();
			 ZipOutputStream zos = new ZipOutputStream(aos)) {

			for (String objectName : objectNames) {
				// 获取对象
				InputStream inputStream = minioClient.getObject(
						GetObjectArgs.builder()
								.bucket(bucketName)
								.object(objectName)
								.build());

				// 创建一个新的ZIP条目，并将文件写入压缩包
				ZipEntry zipEntry = new ZipEntry(objectName);
				zos.putNextEntry(zipEntry);

				byte[] buffer = new byte[8192];
				int length;
				while ((length = inputStream.read(buffer)) > 0) {
					zos.write(buffer, 0, length);
				}

				// 关闭当前的ZIP条目
				zos.closeEntry();
				inputStream.close();
			}

			// 关闭 ZIP 输出流
			zos.close();

			// 将压缩包的字节数组作为响应体返回
			byte[] zipBytes = aos.toByteArray();

			//设置请求头
			HttpHeaders headers = new HttpHeaders();
			headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=output.zip");

			//开始下载
			return ResponseEntity.ok()
					.headers(headers)
					.contentLength(zipBytes.length)
					.contentType(MediaType.APPLICATION_OCTET_STREAM)
					.body(zipBytes);
		} catch (Exception e) {
			throw new RuntimeException("文件下载失败", e);
		}
	}

}
